﻿using System;
using System.IdentityModel.Tokens;
using Sinacor.Infra.Common.Security.Authentication.Credentials;
using System.Security;
using System.Text.RegularExpressions;
using System.Xml;
using System.IO;
using System.Net.Mail;
using PSE.Framework.ProviderLoader;
using PSE.Framework.ProviderLoader.Configuration;
using PSE.Framework.Mail;

namespace Sinacor.Infra.Service.Security.Authentication.ServiceCredentials.Providers
{
    /// <summary>
    /// Provider responsável pela autenticação do Token de segurança
    /// </summary>
    public class SinacorSecurityTokenAuthenticationProvider : BaseProvider, ISecurityTokenAuthenticationProvider
    {
        public SinacorSecurityTokenAuthenticationProvider(ProviderConfigurationElement configuration)
            : base(configuration)
        {

		}

        #region ISecurityTokenAuthenticationProvider Members

        /// <summary>
        /// Efetua a validação do Token
        /// </summary>
        /// <param name="token"><see cref="SecurityToken"/></param>
        /// <returns>Se o Token enviado é Valido ou não</returns>
        public bool ValidateToken(SecurityToken token)
        {
            SinacorToken sinacorToken = token as SinacorToken;

            if (sinacorToken != null)
            {
                AccessControl ac = new AccessControl();
                if (ac.CheckUserPassword(sinacorToken.UserInfo.UserName, sinacorToken.UserInfo.Password))
                    //Verifica se a empresa foi passada no token
                    if (sinacorToken.UserInfo.CompanyId != 0)
                    {
                        //Caso positivo, verificar çse o usuário tem acesso a empresa
                        if (ac.CheckUserAcessToCompany(sinacorToken.UserInfo.UserName, sinacorToken.UserInfo.CompanyId))
                            return true;
                        else
                            return false;
                    }
                    else
                        return true;

                else
                    return false;
            }
            else
                throw new SecurityTokenValidationException("The requested token is not a SinacorToken");
        }

        public bool VerifyUserLogin(string login)
        {
            AccessControl ac = new AccessControl();
            return ac.RetrieveUser(login) != null;
        }

		public bool ValidateUserLogin(string login, string password)
		{
			AccessControl ac = new AccessControl();
			return ac.RetrieveUserPassword(login, password) != null;
		}

        public bool ChangePassword(string login, string password, string newPassword)
        {
            AccessControl ac = new AccessControl();
            return ac.ChangePassword(login, password, newPassword);
        }

        public bool VerifyChangePassword(string login)
        {
            bool changePassword = false;
            AccessControl ac = new AccessControl();

            Sinacor.Infra.Service.Security.User user = ac.RetrieveUser(login);

            if (user == null)
            {
                throw new SecurityException(string.Format("Invalid login '{0}'", login));
            }
            else if (user.ExpirationDate != null && user.ExpirationDate < DateTime.Now)
            {
                changePassword = true;
            }
            else
            {
                changePassword = false;
            }

            return changePassword;
        }

		public bool VerifyIsBlocked(string login)
		{
			AccessControl ac = new AccessControl();
			Sinacor.Infra.Service.Security.User user = ac.RetrieveUser(login);

			if (user == null)
				throw new SecurityException(string.Format("Invalid login '{0}'", login));

			return !user.IsActive;
		}

        public bool ResetPassword(string login)
        {
            bool passwordReset = false;
            AccessControl ac = new AccessControl();
            Regex regex = new Regex(@"^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z0-9]{2,4}|[0-9]{1,3})(\]?)$");

            Sinacor.Infra.Service.Security.User user = ac.RetrieveUser(login);

            if (user == null)
            {
                throw new SecurityException(string.Format("Invalid login '{0}'", login));
            }
            else if (string.IsNullOrEmpty(user.Email))
            {
                passwordReset = false;
            }
            else if (!regex.IsMatch(user.Email))
            {
                passwordReset = false;
            }
            else
            {
                string newPassword = Guid.NewGuid().ToString().Split('-')[0];

                if (ac.ResetPassword(login, newPassword))
                {
                    // Enviar via e-mail
                    MailClient client = new MailClient();

                    MailMessage message = new MailMessage();
                    message.To.Add(user.Email);
                    message.Subject = Emails.Emails.ResetPasswordSubject;
                    message.IsBodyHtml = true;

                    /* Processo para criar um e-mail em outro idioma: 
                     * - Retirar o atributo AssemblyKeyFile do AssemblyInfo.cs e assinar a ddl pelas propriedades do projeto
                     * - Criar uma pasta com o código do idioma dentro na pasta Emails, ex: Emails\en-US\
                     * - Colocar o arquivo xslt traduzido nessa pasta, ex: Emails\en-US\ResetPasswordTemplate.xslt
                     * - Criar um arquivo de resource com o código do idioma, ex: Emails\Emails.en-US.resx
                     * - Adicionar uma referencia do arquivo xslt no arquivo de resource
                     * - Ao capturar o corpo do e-mail ele retornará o xslt correspondente a CurrentUICulture
                    */
                    XmlReader bodyTemplate = XmlReader.Create(new StringReader(Emails.Emails.ResetPasswordTemplate));
                    Emails.ResetPasswordData bodyData = new Emails.ResetPasswordData(newPassword);

                    client.Send(message, bodyTemplate, bodyData);
                    passwordReset = true;
                }
                else
                {
                    passwordReset = false;
                }
            }

            return passwordReset;
        }

        #endregion
    }
}
